package life.cqq.jwt.demo;/**
 * Created by Kim QQ.Cong on 2022/7/7 / 14:30
 *
 * @author: CongQingquan
 * @Description:
 */

import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Created by Kim QQ.Cong on 2022-07-07 / 14:30
 *
 * @author: CongQingquan
 * @Description: 入门
 */
public class Quickstart {

    static ObjectMapper objectMapper = new ObjectMapper();

    static Map<String, Object> testDataMap = new HashMap<String, Object>() {
        {
            put("username", "CQQ");
            put("password", "PSW");
        }
    };

    public static void main(String[] args) throws Exception {
        // quickstart();
        // header();
        // payload(); // 如果同时设定payload & claims 会抛出异常
        // 7个默认可选字段已经被封装，直接调用即可
        // customKey();
        expiration();

        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        String result = Jwts.builder()
            .setClaims(testDataMap)
            .signWith(secretKey)
            .compact();
        // 解析
        JwtParser parser = Jwts.parserBuilder().setSigningKey(secretKey).build();
        System.out.println("Header: " + parser.parse(result).getHeader());
        System.out.println("Claims: " + parser.parse(result).getBody());
    }

    /**
     * 1. Claims：对应的就是 Payload部分
     * 2. 官方提供的密匙Key工具类
     */
    private static void quickstart() {
        // 生成
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        String result = Jwts.builder()
            .setClaims(testDataMap)
            .signWith(secretKey)
            .compact();

        // 解析
        JwtParser parser = Jwts.parserBuilder().setSigningKey(secretKey).build();
        Jws<Claims> claimsJws = parser.parseClaimsJws(result);
        Claims claims = claimsJws.getBody();
        System.out.println(claims.getSubject());
        System.out.println(claims.get("username"));
        System.out.println(claims.get("password"));

        // 查看完整的结果内容：在线网站解析
        System.out.println(result);

        // Keys是官方提供的一个帮助我们生成一个复杂强壮密匙的工具类
        System.out.println(Arrays.toString(secretKey.getEncoded()));
    }

    public static void header() throws Exception {
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String result = Jwts.builder()
            .setHeaderParam("header1", "h1v")
            .setHeaderParam("header2", "h2v")
            .setHeader(testDataMap) // 覆盖HeaderParam
            .signWith(secretKey)
            .compact();
        // 查看完整的结果内容：在线网站解析
        System.out.println(result);
    }

    public static void payload() throws Exception {
        //将对象转为JSON串
        String jsonString = objectMapper.writeValueAsString(testDataMap);

        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String result = Jwts.builder()
            .setPayload(jsonString)
            .signWith(secretKey)
            .compact();

        // 查看完整的结果内容：在线网站解析
        System.out.println(result);
    }

    public static void customKey() {
        SecretKey secretKey = new SecretKeySpec("0123456789_0123456789_0123456789".getBytes(),
            SignatureAlgorithm.HS256.getJcaName());
        String result = Jwts.builder()
            .setHeaderParam("header", "h1v")
            .signWith(secretKey)
            .compact();
        // 查看完整的结果内容：在线网站解析
        System.out.println(result);
    }

    /**
     * 设置最大可容许的时间戳，该时间戳作用于解析过期的Token，当过期时长超过该时间戳时会抛出异常。
     *
     * 当解析一个JWT Token的Payload中的expiration字段的时间戳，与当前时间进行比较
     */
    public static void skewSeconds() {
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        System.out.println(simpleDateFormat.format(new Date(System.currentTimeMillis())));

        String sign = Jwts.builder()
            .setClaims(testDataMap)
            .setExpiration(new Date(System.currentTimeMillis() + 1000))
            .signWith(secretKey)
            .compact();

        System.out.println(sign);

        // 设置可解析过期时长 <1 秒的Token
        JwtParser build = Jwts.parserBuilder().setSigningKey(secretKey)
            .setAllowedClockSkewSeconds(1000).build();

        try {
            // 线程休眠2秒使Token过期
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        Date expiration = build.parseClaimsJws(sign).getBody().getExpiration();
        System.out.println(simpleDateFormat.format(expiration));
    }

    /**
     * 一定要注意：获取过期时间戳时，通过Claims.getExpiration() 与 body.get("exp") 获取的结果是不同的。
     *
     * Claims在set/getExpiration时会进行除以1000 以及 乘1000的操作后，调用put("exp", date) 或 get("exp") * 1000。
     */
    public static void expiration() {
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        Date now = new Date();

        String sign = Jwts.builder()
            .setExpiration(now)
            .signWith(secretKey)
            .compact();

        JwtParser build = Jwts.parserBuilder().setSigningKey(secretKey).setAllowedClockSkewSeconds(5).build();
        Claims body = build.parseClaimsJws(sign).getBody();
        Date expiration = body.getExpiration();
        Object exp = body.get("exp");

        System.out.println("now: " + now.getTime());
        System.out.println("expiration: " + expiration.getTime());
        System.out.println("exp: " + exp);
    }
}